
package com.eworking.eim.message;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.muc.MultiUserChat;

import android.content.ContentValues;
import android.content.Context;
import android.os.Handler;

import com.eworking.eim.EIMCallBack;
import com.eworking.eim.EIMChatOptions;
import com.eworking.eim.EIMConfig;
import com.eworking.eim.EIMSessionManager;
import com.eworking.eim.group.EIMGroupManager;
import com.eworking.eim.packet.EIMCustomPacketExtension;
import com.eworking.eim.user.EIMAccountUtils;
import com.eworking.eim.util.EIMLog;
import com.eworking.eim.util.NetUtil;
import com.eworking.eim.xmpp.EIMConnectionListener;
import com.eworking.eim.xmpp.XMPPConnectionManager;

public class EIMMessageManager {
    
    /** 日志输出标记 */
    private static final String _TAG = EIMMessageManager.class.getSimpleName();
    
    /** 每次加载的消息条数 */
    private static final int _DEFAULT_LOAD_MESSAGE_COUNT = 20;
    
    private static EIMMessageManager messageManager = null;
    
    /** XMPP服务器连接管理 */
    private XMPPConnectionManager xmppConnectionManager = null;
    
    /** 所有的消息集合 key:msgId */
    private Map<String, EIMMessage> allMessages = new Hashtable<String, EIMMessage>();
    
    /** 所有的消息会话集合 key:参与者ID或groupname */
    private Map<String, EIMConversation> allConversations = new Hashtable<String, EIMConversation>();
    
    /** 所有的聊天室对象 */
    private Map<String, Chat> allChats = new HashMap<String, Chat>();
    
    /** 聊天室管理 */
    private ChatManager chatManager = null;
    
    private Handler handler = new Handler();
    
    private Context context;
    
    /** 所有的连接监听 */
    private final List<EIMConnectionListener> connectionListeners = Collections
        .synchronizedList(new ArrayList<EIMConnectionListener>());
    
    /** 消息发送线程池 */
    private ExecutorService sendThreadPool = Executors.newCachedThreadPool();
    
    /** 聊天室参数 */
    private EIMChatOptions chatOptions = null;
    
    public static EIMMessageManager getInstance() {
        if (messageManager == null) {
            messageManager = new EIMMessageManager();
        }
        return messageManager;
    }
    
    /**
     * 初始化
     * 
     * @param context
     * @param xmppConnectionManager
     */
    public void init(Context context, XMPPConnectionManager xmppConnectionManager, EIMChatOptions chatOptions) {
        this.context = context;
        this.xmppConnectionManager = xmppConnectionManager;
        this.setChatOptions(chatOptions);
        try {
            this.chatManager = xmppConnectionManager.getConnection().getChatManager();
            this.chatManager.addChatListener(new EIMChatManagerListener());
            // 设置连接监听
            xmppConnectionManager.setChatConnectionListener(new ChatConnectionListener());
        } catch (Exception e) {
            return;
        }
    }
    
    /**
     * 发送消息
     * 
     * @param message 消息实体
     * @param callBack
     */
    public void sendMessage(EIMMessage message, EIMCallBack callBack) {
        // 如果是群组消息
        if (message.getChatType() == EIMMessage.ChatType.GroupChat) {
            this.sendGroupMessage(message, callBack);
            return;
        }
        try {
            String msgId = message.getMsgId();
            if (msgId == null) {
                // 生成msgId
                msgId = getUniqueMessageId();
                message.setMsgId(msgId);
            }
            // 如果消息缓存中没有该消息，则保存
            if (!this.allMessages.containsKey(msgId)) {
                // 保存到数据库中
                EIMMessageTable.getInstance().insertMessage(message);
                // 将消息加入到会话中
                this.addMessage(message);
            }
            
            // 检查连接是否正常
            XMPPConnectionManager.getInstance().checkConnection();
            // 设置消息的状态为 --- 发送中
            message.setStatus(EIMMessage.Status.INPROGRESS);
            // 设置消息发送人为 当前用户
            message.from = EIMSessionManager.getInstance(null).getCurrentUser();
            String strToUser = message.to.getJID();
            if (!strToUser.contains("@")) {
                strToUser = strToUser + "@" + EIMConfig.DOMAIN;
            }
            Chat chat = this.allChats.get(strToUser);
            if (chat == null) {
                this.chatManager.createChat(strToUser, null);
            }
            // 开启消息发送线程
            EIMSendMessageThread sendMessageThread = new EIMSendMessageThread(chat, message, callBack);
            this.sendThreadPool.execute(sendMessageThread);
        } catch (Exception e) {
            EIMLog.e(_TAG, "发送消息失败:" + message.getMsgId());
            // 消息发送失败
            message.setStatus(EIMMessage.Status.FAIL);
            ContentValues values = new ContentValues();
            values.put(EIMMessageTable.Fields._MSG_STATUS, message.status.ordinal());
            EIMMessageTable.getInstance().updateMessage(message.getMsgId(), values);
            if (callBack != null) {
                callBack.onError(EIMCallBack.ERROR_SEND, "消息发送失败");
            }
        }
    }
    
    /**
     * 发送消息
     * 
     * @param message 消息实体
     * @param callBack
     */
    public void sendGroupMessage(EIMMessage message, EIMCallBack callBack) {
        try {
            message.setChatType(EIMMessage.ChatType.GroupChat);
            if (message.msgId == null) {
                // 生成msgId
                message.msgId = getUniqueMessageId();
            }
            // 如果消息缓存中没有该消息，则保存
            if (!this.allMessages.containsKey(message.msgId)) {
                EIMMessageTable.getInstance().insertMessage(message);
                // 将消息加入到会话中
                this.addMessage(message);
            }
         // 检查连接是否正常
            XMPPConnectionManager.getInstance().checkConnection();
            // 设置消息的状态为 --- 发送中
            message.setStatus(EIMMessage.Status.INPROGRESS);
            // 设置消息发送人为 当前用户
            message.from = EIMSessionManager.getInstance(null).getCurrentUser();
            // 获取群组聊天室
            String endAccount = EIMAccountUtils.getJIDFromAccount(message.getTo());
            MultiUserChat multiUserChat = EIMGroupManager.getInstance().getMUC(endAccount);
            // 开启消息发送线程
            EIMSendMessageThread sendMessageThread = new EIMSendMessageThread(multiUserChat, message, callBack);
            this.sendThreadPool.execute(sendMessageThread);
        } catch (Exception e) {
            EIMLog.e(_TAG, "发送消息失败:" + message.getMsgId());
            // 消息发送失败
            message.setStatus(EIMMessage.Status.FAIL);
            ContentValues values = new ContentValues();
            values.put(EIMMessageTable.Fields._MSG_STATUS, message.status.ordinal());
            EIMMessageTable.getInstance().updateMessage(message.getMsgId(), values);
            if (callBack != null) {
                callBack.onError(EIMCallBack.ERROR_SEND, "消息发送失败");
            }
        }
    }
    
    /**
     * 将消息放入缓存，并加入到会话中
     * 
     * @param message 消息实体
     */
    private void addMessage(EIMMessage message) {
        // 将消息放入缓存
        this.allMessages.put(message.getMsgId(), message);
        // 如果是群组消息
        if (message.getChatType() == EIMMessage.ChatType.GroupChat) {
            // 将消息添加到当前参与者或群组的会话中
            this.getConversation(message.getTo()).addMessage(message);
        } else {
            // 如果是接收的消息
            if (message.getDirect() == EIMMessage.Direct.RECEIVE) {
                this.getConversation(message.getFrom()).addMessage(message);
            } else {
                // 如果是发送的消息
                this.getConversation(message.getTo()).addMessage(message);
            }
        }
        
    }
    
    /**
     * 保存消息信息
     * 
     * @param message 消息实体
     */
    public void saveMessage(EIMMessage message) {
        try {
            this.addMessage(message);
            EIMMessageTable.getInstance().insertMessage(message);
        } catch (Exception e) {
            EIMLog.e(_TAG, "保存消息信息失败：" + e.getMessage());
        }
    }
    
    /**
     * 获取消息信息
     * 
     * @param msgId 消息ID
     * @return
     */
    public EIMMessage getMessage(String msgId) {
        return this.allMessages.get(msgId);
    }
    
    /**
     * 确认消息已读，发送已读回执
     */
    public void ackMessageRead(String account, String msgId) {
        // 是否需要回执
        if (!this.getChatOptions().getRequireAck()) {
            EIMLog.d(_TAG, "聊天室参数设置不需要回执，请确认是否需要回执");
            return;
        }
        try {
        	// 检查连接是否正常
            XMPPConnectionManager.getInstance().checkConnection();
            // JID
            String JID = EIMAccountUtils.getJIDFromAccount(account);
            Chat chat = this.allChats.get(JID);
            if (chat == null) {
                chat = this.chatManager.createChat(JID, null);
            }
            Message message = new Message();
            // 消息扩展
            EIMCustomPacketExtension customPacketExtension =
                new EIMCustomPacketExtension(EIMCustomPacketExtension._ACKED);
            customPacketExtension.setValue(EIMCustomPacketExtension._ID, msgId);
            message.addExtension(customPacketExtension);
            
            message.setBody(msgId);
            message.setType(Message.Type.normal);
            message.setTo(JID);
            String strFrom = EIMSessionManager.getInstance(this.context).getCurrentUser().getJID();
            message.setFrom(strFrom);
            // 发送回执消息
            this.xmppConnectionManager.getConnection().sendPacket(message);
            // 更新数据库
            EIMMessageTable.getInstance().updateMessageAck(msgId, true);
            EIMLog.d(_TAG, "发送已读回执消息,消息ID：" + msgId);
        } catch (Exception e) {
            EIMLog.d(_TAG, "发送已读回执消息失败：" + e.getMessage());
        }
    }
    
    /**
     * 加载所有会话及消息
     */
    public void loadAllConverstations() {
        // 获取所有消息中参与人(单聊)
        List<String> lstParticipants = EIMMessageTable.getInstance().queryAllParticipantsFromMsg();
        for (String participant : lstParticipants) {
            // 获取与各参与人的消息记录
            List<EIMMessage> allPartMessages =
                EIMMessageTable.getInstance().queryMessages(participant, null, _DEFAULT_LOAD_MESSAGE_COUNT);
            // 创建当前参与人的消息会话实体
            EIMConversation conversation = new EIMConversation(participant, allPartMessages);
            // 将消息会话放入缓存
            this.allConversations.put(participant, conversation);
            
            // 将消息记录载入缓存
            for (EIMMessage message : allPartMessages) {
                this.allMessages.put(message.getMsgId(), message);
            }
        }
        // 获取所有消息中的群组(群聊)
        List<String> lstGroups = EIMMessageTable.getInstance().queryAllGroupsFromMsg();
        for (String groupName : lstGroups) {
            // 获取与各群组的消息记录
            List<EIMMessage> allGroupMessages =
                EIMMessageTable.getInstance().queryGroupMessages(groupName, null, _DEFAULT_LOAD_MESSAGE_COUNT);
            // 创建当前群组的消息会话实体
            EIMConversation conversation = new EIMConversation(groupName, allGroupMessages);
            // 将群组消息会话放入缓存
            this.allConversations.put(groupName, conversation);
            
            // 将消息记录载入缓存
            for (EIMMessage message : allGroupMessages) {
                this.allMessages.put(message.getMsgId(), message);
            }
        }
    }
    
    /**
     * 获取当前参与人或群组的消息会话
     * 
     * @param conversKey 参与人或群组ID
     * @return
     */
    public EIMConversation getConversation(String conversKey) {
        // 根据参与者ID或群组ID获取会话对象
        EIMConversation conversation = this.allConversations.get(conversKey);
        if (conversation == null) {
            conversation = new EIMConversation(conversKey);
            this.allConversations.put(conversKey, conversation);
        }
        return conversation;
    }
    
    /**
     * 删除会话及消息
     * 
     * @param conversKey 参与者ID或群组ID
     */
    public void deleteConversation(String conversKey) {
        // 根据参与者ID或群组ID获取会话对象
        EIMConversation conversation = this.allConversations.get(conversKey);
        if (conversation != null) {
            // 从数据库删除
            EIMMessageTable.getInstance().deleteConversions(conversKey);
            conversation.clear();
            this.allConversations.remove(conversKey);
        }
    }
    
    /**
     * 清空当前参与者或群组的会话消息记录
     * 
     * @param conversKey 参与者ID或群组ID
     */
    public void clearConversation(String conversKey) {
        // 根据参与者ID或群组ID获取会话对象
        EIMConversation conversation = this.allConversations.get(conversKey);
        if (conversation != null) {
            // 从数据库删除
            EIMMessageTable.getInstance().deleteConversions(conversKey);
            conversation.clear();
        }
    }
    
    /**
     * 获取所有未读取的消息数量
     * 
     * @return
     */
    public int getUnreadMsgsCount() {
        int unreadMsgsCount = 0;
        Iterator<EIMConversation> iterator = this.allConversations.values().iterator();
        while (iterator.hasNext()) {
            EIMConversation conversation = iterator.next();
            unreadMsgsCount += conversation.getUnreadMsgCount();
        }
        EIMLog.d(_TAG, "获取所有未读取的消息数量：" + unreadMsgsCount);
        return unreadMsgsCount;
    }
    
    /**
     * 清空缓存
     */
    public void clear() {
        this.allChats.clear();
        this.allConversations.clear();
        this.allMessages.clear();
    }
    
    /**
     * 生成MsgID
     * 
     * @return String msgID
     */
    private static String getUniqueMessageId() {
        String str = Long.toHexString(System.currentTimeMillis());
        str = str.substring(6);
        return Packet.nextID() + "-" + str;
    }
    
    /**
     * 聊天室监听器，当创建聊天室时，将聊天室对象放入缓存
     * 
     * @author yangsong
     * @since 1.0
     * @version 2014年7月16日
     */
    private class EIMChatManagerListener implements ChatManagerListener {
        
        @Override
        public void chatCreated(Chat chat, boolean flag) {
            String strParticipant = chat.getParticipant();
            // 放入缓存
            allChats.put(strParticipant, chat);
        }
    }
    
    /**
     * XMPP连接监听器
     * 
     * @author yangsong
     * @since 1.0
     * @version 2014年7月16日
     */
    private class ChatConnectionListener implements ConnectionListener {
        
        @Override
        public void connectionClosed() {
            EIMLog.d(_TAG, "关闭XMPP服务器连接");
            handler.post(new Runnable() {
                
                @Override
                public void run() {
                    for (EIMConnectionListener connectionListener : connectionListeners) {
                        if (connectionListener != null) {
                            connectionListener.onDisConnected("connectionClosed");
                        }
                    }
                }
            });
        }
        
        @Override
        public void connectionClosedOnError(Exception arg0) {
            EIMLog.d(_TAG, "关闭XMPP服务器连接错误");
            if (xmppConnectionManager != null && !NetUtil.hasDataConnection(context)) {
                xmppConnectionManager.registerConnectivityReceiver();
            }
            EIMGroupManager.getInstance().removeMucs();
            handler.post(new Runnable() {
                
                @Override
                public void run() {
                    for (EIMConnectionListener connectionListener : connectionListeners) {
                        if (connectionListener != null) {
                            connectionListener.onDisConnected("connectionClosed");
                        }
                    }
                }
            });
        }
        
        @Override
        public void reconnectingIn(int arg0) {
            
        }
        
        @Override
        public void reconnectionFailed(Exception arg0) {
            EIMLog.d(_TAG, "重新连接XMPP服务器失败");
            handler.post(new Runnable() {
                
                @Override
                public void run() {
                    for (EIMConnectionListener connectionListener : connectionListeners) {
                        if (connectionListener != null) {
                            connectionListener.onDisConnected("connectionClosed");
                        }
                    }
                }
            });
        }
        
        @Override
        public void reconnectionSuccessful() {
            EIMLog.d(_TAG, "重新连接XMPP服务器");
            handler.post(new Runnable() {
                
                @Override
                public void run() {
                    for (EIMConnectionListener connectionListener : connectionListeners) {
                        if (connectionListener != null) {
                            connectionListener.onReConnected();
                        }
                    }
                }
            });
        }
    }
    
    public void addConnectionListener(final EIMConnectionListener connectionListener) {
        if (connectionListener != null) {
            this.connectionListeners.add(connectionListener);
            if ((this.xmppConnectionManager != null) && (this.xmppConnectionManager.getConnection() != null)
                && (this.xmppConnectionManager.getConnection().isConnected()))
                this.handler.post(new Runnable() {
                    
                    public void run() {
                        
                        for (EIMConnectionListener tempConnectionListener : connectionListeners) {
                            if (tempConnectionListener != null && tempConnectionListener.equals(connectionListener)) {
                                tempConnectionListener.onConnected();
                            }
                        }
                    }
                });
            else if (this.xmppConnectionManager != null)
                this.xmppConnectionManager.registerConnectivityReceiver();
        }
    }
    
    public void removeConnectionListener(EIMConnectionListener connectionListener) {
        if (connectionListener != null) {
            this.connectionListeners.remove(connectionListener);
        }
    }

	public EIMChatOptions getChatOptions() {
		return chatOptions;
	}

	public void setChatOptions(EIMChatOptions chatOptions) {
		this.chatOptions = chatOptions;
	}
}
